#ifndef __PROCESS_POOL_HPP__
#define __PROCESS_POOL_HPP__

#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
#include <unistd.h>
#include <sys/wait.h>
#include "task.hpp"

const int default_num = 5;

//描述一个子进程
class Channel
{
public:
    Channel(int wfd, pid_t id)
        :_wfd(wfd)
        , _id(id)
    {
        _name = "channel-" + std::to_string(_wfd) + "-" + std::to_string(_id); 
    }

    //发送任务
    void Send(int taskcode)
    {
        write(_wfd, &taskcode, sizeof taskcode);
    }

    //关闭写入子进程的对应管道
    void Close()
    {
        close(_wfd);
    }

    //回收该子进程
    void Wait()
    {
        waitpid(_id, nullptr, 0);
    }

    int Fd() { return _wfd; }
    pid_t Id() { return _id; }
    const std::string& Name() { return _name; } 
private:
    int _wfd; // 父进程对当前子进程进行写入的管道文件描述符
    pid_t _id; // 当前子进程的pid
    std::string _name;
};

// 组织
class ChannelManager
{
public:
    //将创建的进程插入进程表
    void Insert(int wfd, pid_t id)
    {
        _channels.push_back({wfd, id});
    }

    //打印进程表
    void PrintChannels()
    {
        for(auto& c : _channels)
        {
            std::cout << c.Fd() << ' ' << c.Id() << ' ' << c.Name() << std::endl;
        }
    }

    //选择进程
    Channel& Select()
    {
        Channel& c = _channels[_next];
        _next++;
        _next %= _channels.size();
        return c;
    }

    //用于子进程关闭指向其他子进程的写端指针
    void CloseAll()
    {
        for(auto& c : _channels)
        {
            c.Close();
        }
    }

    //关闭所有管道
    void StopProcess()
    {
        for(auto& c : _channels)
        {
            std::cout << "关闭" << c.Name() << std::endl;
            c.Close();
        }
    }

    //回收所有子进程
    void WaitProcess()
    {
        for(auto& c : _channels)
        {
            std::cout << "回收" << c.Name() << std::endl;
            c.Wait();
        }
    }
private:
    std::vector<Channel> _channels;
    int _next = 0;
};

// 进程池定义
class ProcessPool
{
public:
    ProcessPool(int num)
        : _process_num(num)
    {
        //注册任务
        _tm.Register(PrintLog);
        _tm.Register(Download);
        _tm.Register(Upload);
    }

    //子进程读取任务并执行
    void Work(int rfd)
    {
        while(true)
        {
            int code = 0;
            ssize_t n = read(rfd, &code, sizeof code);
            if(n > 0) // 读取成功
            {
                if(n != sizeof code) // 读取不规范
                {
                    continue;
                }
                std::cout << "子进程" << getpid() << "收到一个任务码:" << code << std::endl;
                _tm.Execute(code);
                std::cout << "----------------------------" << std::endl;
            }
            else if(n == 0) // 读取到文件末尾
            {
                std::cout << "子进程" << getpid() << "读取结束" << std::endl;
                break;
            }
            else // 读取失败
            {
                std::cout << "子进程" << getpid() <<  "读取错误" << std::endl;
                break;
            }
        }
    }

    //启动进程池
    bool Start()
    {
        for (int i = 0; i < _process_num; i++)
        {
            // 创建管道
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
                return false;

            // 创建子进程
            pid_t id = fork();
            if (id < 0)
                return false;
            if (id == 0) // 子进程
            {
                //关闭指向其他子进程的写端指针
                _cm.CloseAll();

                close(pipefd[1]);

                Work(pipefd[0]);
                close(pipefd[0]);
                exit(0);
            }
            else // 父进程
            {
                close(pipefd[0]);
                _cm.Insert(pipefd[1], id);
            }
        }
        return true;
    }

    void Debug()
    {
        _cm.PrintChannels();
    }

    //选择子进程，并发送任务
    void Run()
    {
        //选择一个任务
        int taskcode = _tm.Code();

        //负载均衡地选择一个子进程，完成任务
        Channel& c = _cm.Select();
        std::cout << "选择了子进程:" << c.Name() << std::endl;

        //发送任务
        c.Send(taskcode);
    }

    //停止工作
    void Stop()
    {
        //关闭父进程所有的任务管道
        _cm.StopProcess();
        
        usleep(10000);

        //回收子进程
        _cm.WaitProcess();
    }
private:
    ChannelManager _cm; // 信道表
    int _process_num; // 子进程数
    TaskManager _tm; // 任务表
};

#endif